#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <stdint.h>
#include <time.h>
#include "simulation.h"
#define NUMRUNS 30

double average(vector<double> input) {
    double mean = 0.0;
    for (uint32_t index = 0; index < input.size(); ++index) {
        mean += input[index];
    }
    mean /= input.size();
    return mean;
}

double stdev(vector<double> input, double mean) {
    double var = 0.0;
    for (uint32_t index = 0; index < input.size(); ++index) {
        var += ((input[index] - mean) * (input[index] - mean));
    }
    var /= (input.size()-1);
    return sqrt(var);
}

void test1() {
    vector<double> input;
    for (uint32_t index = 0; index < 10; ++index) {
        input.push_back(index);
    }
    cout << "mean = " << average(input) << endl;
    cout << "stdev = " << stdev(input, average(input)) << endl;
}

void get_mean_conf_delta(vector<double> data, double *databar, double *conf_delta) {
    *databar = average(data);
    double datastdev = stdev(data, *databar);
    *conf_delta = 1.96 * datastdev / sqrt(30);
}

void test2() {
    long seed = 50;
    for (uint32_t i = 0; i < 10; ++i) {
        cout << Simulation::pareto(&seed) << " "; 
    }
    cout << endl;
    for (uint32_t i = 0; i < 10; ++i) {
        cout << Simulation::expntl(3000, seed) << " "; 
    }
    cout << endl;

}

int main(int argc, char **argv){
    if (argc != 6) {
        cout << "./proj3 <lambda> <C> <L> <M> <type>" << endl
             << "<lambda> - The arrival rate" << endl
             << "<C> - The number of customers to service" << endl
             << "<L> - L = 0 : FCFS, L = 1 : SJF" << endl
             << "<M> - M = 0 : Exponentially distributed, M = 1 : Pareto distributed ; service times" << endl
             << "<type> - type = 0 : M/<M/G>/1 system (task 4), type = 1 : M/<M/G>/3 system (task 3)" << endl;
        return -1;
    }
    Simulation *simulation;
    uint32_t l = atoi(argv[3]);
    uint32_t m = atoi(argv[4]);
    uint32_t type = atoi(argv[5]);
    vector<double> avg_system_times, avg_waiting_times, avg_service_times;
    vector<double> running_times;
    vector<double> clrs;
    double start_time, end_time;
    start_time = clock();
    if (type == 0) {
        if (l == 0) {
            simulation = new FCFSSimulation(argc, argv);
        } else if (l == 1) {
            simulation = new SJFSimulation(argc, argv);
        } else {
            cout << "Invalid value of L. L can either be 0 or 1." << endl;
        }
        cout << "Run 1 " << endl;
        simulation->run();
        simulation->calculate_statistics();
        avg_system_times.push_back(simulation->avg_system_time);
        avg_waiting_times.push_back(simulation->avg_waiting_time);
        avg_service_times.push_back(simulation->avg_service_time);
        clrs.push_back(simulation->clr);       	
    } else if (type == 1) {
        for (uint32_t index = 0; index < NUMRUNS; ++index) {
            cout << "Run " << index + 1 << endl;
            if (l == 0) {
                simulation = new FCFSSimulation(argc, argv);
            } else if (l == 1) {
                simulation = new SJFSimulation(argc, argv);
            } else {
                cout << "Invalid value of L. L can either be 0 or 1." << endl;
            }
            simulation->run();
            simulation->calculate_statistics();
            avg_system_times.push_back(simulation->avg_system_time);
            avg_waiting_times.push_back(simulation->avg_waiting_time);
            avg_service_times.push_back(simulation->avg_service_time);
            clrs.push_back(simulation->clr);       	
        }
    }
    end_time = clock();				// simulation end timer
    running_times.push_back(1.0 * (end_time - start_time) / CLOCKS_PER_SEC);

    cout << "Value of the input parameter lambda = " << simulation->lambda << endl;
    cout << "Value of the input parameter C = " << simulation->c << endl;
    cout << "Value of the master clock at the end of the simulation = " 
        << simulation->sim_time << endl;
    double mean, conf_delta;
    get_mean_conf_delta(avg_service_times, &mean, &conf_delta);
    cout << "Average service time = " << mean << " Confidence Interval ("
        << mean - conf_delta << ", "
        << mean + conf_delta << ")" << endl;

    get_mean_conf_delta(avg_system_times, &mean, &conf_delta);
    cout << "Average system time = " << mean << " Confidence Interval ("
        << mean - conf_delta << ", "
        << mean + conf_delta << ")" << endl;

    get_mean_conf_delta(avg_waiting_times, &mean, &conf_delta);
    cout << "Average waiting time = " << mean << " Confidence Interval ("
        << mean - conf_delta << ", "
        << mean + conf_delta << ")" << endl;

}
